package AwesomeVersion;

import java.net.InetAddress;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

/**
 * @author M-a-g-n-u-s
 */
public class SocketAction extends UnicastRemoteObject implements GameInterface,
		Runnable {

	private static final long serialVersionUID = 1L;
	private Registry registry;
	private GameInterface rmiServer;
	private TicTacToeGui gui;

	private String myIp;
	private int myPort = 1337; // default port
	private char myMark;
	private String myName;
	private String myRegistryName;
	private int[] myCurrentMove = new int[3]; // (0:isSet,1:x,2:y) --isSet
												// indicates that the array is
												// set by player if value = 1

	private String remoteIp;
	private int remotePort;
	private String remoteRegistryName;
	private int[] remoteCurrentMove = new int[3], remoteLastMove = new int[3];

	private boolean connectionStatus = false;
	private boolean iAmServer = false;
	private boolean myTurn = false;

	private Thread listener;

	/**
	 * @param name
	 * @param remIP
	 * @param port
	 * @throws RemoteException
	 * @throws Exception
	 * 
	 *             Checks if a game-server is running on the IP and port
	 *             received from parameter. If this is not the case, start a
	 *             local server and wait for another player to connect
	 * 
	 */
	protected SocketAction(String remIP, int remPort, TicTacToeGui tempGui)
			throws RemoteException, Exception {

		super();
		gui = tempGui;
		remoteIp = remIP;
		remotePort = remPort;
		remoteRegistryName = remoteIp + ":" + remotePort;
		myIp = (InetAddress.getLocalHost().getHostAddress());
		myName = tempGui.getMyName();

		try { // Try to connect
			registry = LocateRegistry.getRegistry(remoteIp, (new Integer(
					remotePort)).intValue());
			setRmiServer((GameInterface) (registry.lookup(remoteRegistryName)));
			connectionStatus = true;
			myMark = 'O';
			listener = new Thread(this); // Run client listener in separate
											// thread
			listener.start();
			// runAsClient();
		} catch (RemoteException e) {
			System.out.println("No server found @ IP: " + remoteIp + " Port: "
					+ remotePort + " Registryname: " + remoteRegistryName);
		} catch (NotBoundException e) {
			System.err.println(e);
		}

		if (!connectionStatus) {
			runAsServer();
		}

	}

	/**
	 * Starts a Registry server, binded with an auto-generated registryName. The
	 * player then acts as the server, waiting for another player to connect
	 */
	private void runAsServer() {
		myRegistryName = myIp + ":" + myPort;
		myMark = 'X';
		myTurn = true;
		iAmServer = true;
		try {
			registry = LocateRegistry.createRegistry(myPort);
			registry.rebind(myRegistryName, this);
		} catch (RemoteException e) {
			System.out.println("remote exception" + e);
		}
	}

	/**
	 * @throws RemoteException
	 *             Starts the game-interface after successfully connecting to a
	 *             running server. Player is then assigned as Player 2 (mark:
	 *             O);
	 */
	private void runAsClient() throws RemoteException {
		myTurn = false;
		getRmiServer().connectToServer(myName);
		listenAsClient();

	}

	private void listenAsClient() throws RemoteException {
		while (connectionStatus) {
			// System.out.println("connStatus" + connectionStatus + " MyTurn: "
			// + myTurn);
			while (!myTurn) {
				try {
					remoteCurrentMove = getRmiServer().sendToClient();
					// System.out.println(remoteCurrentMove[0] + " - " +
					// remoteCurrentMove[1] + " - " +remoteCurrentMove[2]);
					if (remoteCurrentMove != null
							&& remoteCurrentMove[0] == 1
							&& remoteCurrentMove.toString() != remoteLastMove
									.toString()) {
						// System.out.println("listenAsClient():"+
						// remoteCurrentMove[0]+ ":" + remoteCurrentMove[1]+
						// " : " + remoteCurrentMove[2]);
						gui.setMark(remoteCurrentMove[1], remoteCurrentMove[2],
								(myMark == 'O') ? 'X' : 'O');
						remoteLastMove = remoteCurrentMove;
						gui.println("Your turn!");
						myTurn = true;
					}
				} catch (Exception e) {
					System.err.println();
				}

				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
				}
			}
		}
	}

	// Getters & Setters

	/**
	 * @return Returns the assigned player-mark
	 */
	public char getMyMark() {
		return this.myMark;
	}

	/**
	 * @return Returns the current user-IP
	 */
	public String getMyIP() {
		return this.myIp;
	}

	/**
	 * @return Returns the current player-Port
	 */
	public int getMyPort() {
		return this.myPort;
	}

	/**
	 * @return Returns the current user-RegistryName
	 */
	public String getMyRegistryName() {
		return this.myRegistryName;
	}

	/**
	 * @return boolean Returns the current connection status for the player
	 */
	public boolean getMyConnectionStatus() {
		return this.connectionStatus;
	}

	/**
	 * @return Returns the current remote-IP
	 */
	public String getRemoteIP() {
		return this.remoteIp;
	}

	/**
	 * @return Returns the current remote-Port
	 */
	public int getRemotePort() {
		return this.remotePort;
	}

	/**
	 * @return boolean returns true if you are allowed to make a turn
	 */
	public boolean getMyTurn() {
		return this.myTurn;
	}

	/**
	 * @param bolVal
	 *            Sets the myTurn variabel to parameter value
	 */
	public void setMyTurn(boolean bolVal) {
		this.myTurn = bolVal;
	}

	public void setMyCurrentMove(int isSet, int a, int b) {
		myCurrentMove[0] = isSet;
		myCurrentMove[1] = a;
		myCurrentMove[2] = b;
	}

	public int[] getMyCurrentMove() {
		return myCurrentMove;
	}

	/**
	 * @return boolean Returns true if user is Server
	 */
	public boolean getIAmServer() {
		return iAmServer;
	}

	public GameInterface getRmiServer() {
		return rmiServer;
	}

	public void setRmiServer(GameInterface rmiServer) {
		this.rmiServer = rmiServer;
	}

	// Specific Remote Action methods -------------------------------------
	@Override
	public void sendToServer(int[] moveAction) throws RemoteException {
		System.out.println("sendToServer(): activated");
		setMyTurn(true);
		remoteCurrentMove = moveAction;
		System.out.println(remoteCurrentMove[0] + " - " + remoteCurrentMove[1]
				+ " - " + remoteCurrentMove[2]);
		if (moveAction[0] == 1) {
			gui.setMark(moveAction[1], moveAction[2], (myMark == 'X') ? 'O'
					: 'X');
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see AwesomeVersion.GameInterface#sendAction(int, int) Sends action from
	 * Server to Client
	 */
	public int[] sendToClient() throws RemoteException {
		System.out.println("sendToClient():" + myCurrentMove[0] + "-"
				+ myCurrentMove[1] + "-" + +myCurrentMove[2]);
		if (connectionStatus && myTurn && myCurrentMove[0] == 1) {
			setMyTurn(false);
		}
		return myCurrentMove;
	}

	@Override
	public void connectToServer(String name) throws RemoteException {
		connectionStatus = true;
		setMyTurn(true);
		gui.println(name + " is connected!");
	}

	@Override
	public void run() {
		try {
			if (!this.getIAmServer()) {
				runAsClient();
			}
		} catch (RemoteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
